/*******************************************************************************
 *
 * File:        LEDMAN.c
 * Progetto:    Robottino
 * Ambiente:    PIC32
 * Data:        14/03/2012
 * Autore:      M. Tili
 * Versione:    00.01
 *
 * Descrizione: 
 *   Modulo di controllo dei LED.
 *
 *
 * Modifica:  22/05/2012  | M.Tili
 *                        | Rework del modulo a seguito di modifiche in keyman.
 *
 */
#include "CONFIG.h"
#include "TYPEDEFS.h"
#include "GLOBVAR.h"
#include "TIMER.h"
#include "LEDMAN.h"



/*___DEFINIZIONE TIPI E VARIABILI PRIVATE_____________________________________*/

typedef struct tcmdstat {
  e_led_cmd_id id;
  e_cmd_state  state;
}t_cmd_stat;

/* max 1 comando per led */
static t_cmd_stat ledCmdStat[LED_ID_MAX] = {
/*               CMD_ID       CMD_STATE   */
/*LED_ID_00*/ {LED_CMD_NONE, CMD_NOT_SET},
/*LED_ID_01*/ {LED_CMD_NONE, CMD_NOT_SET},
/*LED_ID_02*/ {LED_CMD_NONE, CMD_NOT_SET}
};


/* Il tipo e_tmr_blink_id e la variabile timerIdByLedId servono come lookup 
   table nelle funzioni statiche di questo modulo, per una selezione automatica
   del timer a partire dall'id del led.
 */
typedef enum {
  EN_T_BLINK_PERIOD = 0,
  EN_T_BLINK_LENGHT = 1,
  EN_T_BLINK_MAX    = 2
} e_tmr_blink_id;

static e_timer_id timerIdByLedId[LED_ID_MAX][EN_T_BLINK_MAX] = {
/*              EN_T_BLINK_PERIOD    EN_T_BLINK_REPETION       */
/*LED_ID_00*/ { TIMER_LED00_BLINK,   TIMER_LED00_ONOFF },
/*LED_ID_01*/ { TIMER_LED00_BLINK,   TIMER_LED00_ONOFF },
/*LED_ID_02*/ { TIMER_LED00_BLINK,   TIMER_LED00_ONOFF }
};

/*___DEFINIZIONE FUNZIONI PRIVATE_____________________________________________*/

static void LedManUpdtOneLed(e_led_id ledIDX,
                             e_timer_id tmrOnOff,
                             e_timer_id tmrBlink);



/*___FINE DELLE DEFINIZIONI___________________________________________________*/
/*============================================================================*/


/*******************************************************************************
 *
 *  LedManInit
 *  Descrizione: Questa funzione inizializza i dati del modulo LEDMAN ed i timer
 *               associati.
 *
 */
void LedManInit(void)
{
  int idx;
  for(idx=0;idx <LED_ID_MAX;idx++)
  {
    ledCmdStat[idx].id    = LED_CMD_NONE;
    ledCmdStat[idx].state = CMD_NOT_SET;
    }
  TimerResetById(TIMER_LED00_ONOFF);
  TimerResetById(TIMER_LED00_BLINK);
  TimerResetById(TIMER_LED01_ONOFF);
  TimerResetById(TIMER_LED01_BLINK);
  TimerResetById(TIMER_LED02_ONOFF);
  TimerResetById(TIMER_LED02_BLINK);

  LedManSetCmd(LED_ID_00, LED_CMD_ON);        /* LED per controllo safe state */
}


/*******************************************************************************
 *
 *  LedManSetCmd
 *  Descrizione: Questa funzione imposta il comando per il LED se non c'e' gia'
 *               un comando in corso o in attesa di essere applicato.
 *
 */
BOOLEAN LedManSetCmd(e_led_id ledId, e_led_cmd_id cmdId)
{
  BOOLEAN    retValue;

  retValue = FALSE;

  switch (ledId)
  {
    case LED_ID_00:
    case LED_ID_01:
    case LED_ID_02:
      if ((ledCmdStat[ledId].state != CMD_IN_PROGRESS) &&
          (ledCmdStat[ledId].state != CMD_REQUESTED))
      {
        ledCmdStat[ledId].id    = cmdId;
        ledCmdStat[ledId].state = CMD_REQUESTED;
        retValue = TRUE;
      }
      break;
   default:
      /* DEFENSIVE SHUTDOWN */
     break;
  }
  return retValue;
}


/*******************************************************************************
 *
 *  LedManUpdate
 *  Descrizione: Questa funzione schedula l'aggiornamento dello stato dei 
 *               comandi sui LED (da chiamare ciclicamente).
 *
 */
void LedManUpdate(void)
{
  int idxLED;
  e_led_id ledId;
  for (idxLED;idxLED<LED_ID_MAX;idxLED++)
  {
    switch (idxLED)
    {
      case LED_ID_00:
        ledId = LED_ID_00;
        break;
      case LED_ID_00:
        ledId = LED_ID_00;
        break;
      case LED_ID_00:
        ledId = LED_ID_00;
        break;
      default:
        /* DO NOTHING */
        break;
    }
    switch (ledCmdStat[idxLED].state)
    {
      case CMD_REQUESTED:
        /* prendi in carico il comando per il LED */
        LedManApplyCmd(ledId,ledCmdStat[idxLED].id);
        ledCmdStat[idxLED].state = CMD_IN_PROGRESS;
        break;
      case CMD_IN_PROGRESS:
        /* valuta la fine del comando */
        if (LedManCheckCmd(ledId,ledCmdStat[idxLED].id) == FALSE)
        {
          ledCmdStat[idxLED].state = CMD_IN_PROGRESS;
        }
        else
        {
          ledCmdStat[idxLED].state = CMD_OK; /* o NOT_SET ??? */
        }
        break;
      default:
        /* DO NOTHING */
        break;
    }
  }
}


/*******************************************************************************
 *
 *  LedManApplyCmd
 *  Descrizione: Questa funzione applica un comando sui led.
 *
 */
static void LedManApplyCmd(e_led_id inLedId, e_led_cmd_id inLedCmdId)
{
  switch (inLedCmdId)
  {
    case LED_CMD_NONE:
      /* DO NOTHING */
      break;
    case LED_CMD_BLINK:
      TimerSet(timerIdByLedId[inLedId,EN_T_BLINK_PERIOD], 250,FALSE);
      TimerSet(timerIdByLedId[inLedId,EN_T_BLINK_LENGHT],2000,FALSE);
      ioDataStat.output.ledState[inLedId] = STATE_HIGH;
      break;
    case LED_CMD_OFF:
        ioDataStat.output.ledState[inLedId] = STATE_LOW;
      break;
    case LED_CMD_ON:
        ioDataStat.output.ledState[inLedId] = STATE_HIGH;
        break;
    case LED_CMD_TOGGLE:
      if (ioDataStat.output.ledState[inLedId] != STATE_HIGH)
      {
        ioDataStat.output.ledState[inLedId] = STATE_HIGH;
      }
      else
      {
        ioDataStat.output.ledState[inLedId] = STATE_LOW;
      }
      break;
    default:
      /* DO NOTHING */
      break;
  }
}

/*******************************************************************************
 *
 *  LedManCheckCmd
 *  Descrizione: Questa funzione controlla lo stato di un comando sui led.
 *
 *  Il valore di ritorno indica se il comando applicato deve considerarsi:
 *    - ESEGUITO ----> TRUE;
 *    - IN CORSO ----> FALSE;
 */
static BOOLEAN LedManCheckCmd(e_led_id inLedId, e_led_cmd_id inLedCmdId)
{
  BOOLEAN retValue;
  retValue = FALSE;
  switch (inLedCmdId)
  {
    case LED_CMD_NONE:
      /* DO NOTHING */
      break;
    case LED_CMD_BLINK:
      if ( TimerHasExpired(timerIdByLedId[inLedId,EN_T_BLINK_LENGHT]) == FALSE)
      {
        TimerResetById(timerIdByLedId[inLedId,EN_T_BLINK_LENGHT]);
        TimerResetById(timerIdByLedId[inLedId,EN_T_BLINK_PERIOD]);
        ioDataStat.output.ledState[inLedId] = STATE_LOW;
        retValue =TRUE;
      }
      else
      {
        if ( TimerHasExpired(timerIdByLedId[inLedId,EN_T_BLINK_PERIOD]) == FALSE)
        {
          /* DO NOTHING */
        }
        else
        {
          ioDataStat.output.ledState[inLedId] = 
            (ioDataStat.output.ledState[inLedId] == STATE_HIGH) ?
              STATE_LOW : STATE_HIGH;
          TimerResetById(timerIdByLedId[inLedId,EN_T_BLINK_PERIOD]);
          TimerSet(timerIdByLedId[inLedId,EN_T_BLINK_PERIOD], 250, FALSE);
        }
      }
      break;
    case LED_CMD_OFF:
    case LED_CMD_ON:
    case LED_CMD_TOGGLE:
      retValue = TRUE;
      break;
    default:
      /* DO NOTHING */
      break;
  }
  return retValue;
}

